package device

import (
	"net/http"

	"iot-cloud-platform/internal/auth"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

// DeviceController handles device management endpoints
type DeviceController struct {
	deviceService *DeviceService
}

// NewDeviceController creates a new device controller
func NewDeviceController(deviceService *DeviceService) *DeviceController {
	return &DeviceController{
		deviceService: deviceService,
	}
}

// CreateDevice handles device creation
func (dc *DeviceController) CreateDevice(c *gin.Context) {
	var req CreateDeviceRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	userID, exists := auth.GetCurrentUser(c)
	if !exists {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
		return
	}

	device, err := dc.deviceService.CreateDevice(c.Request.Context(), req, userID)
	if err != nil {
		if err == ErrDeviceAlreadyExists {
			c.JSON(http.StatusConflict, gin.H{"error": "Device already exists"})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create device"})
		}
		return
	}

	c.JSON(http.StatusCreated, device)
}

// GetDevice handles device retrieval
func (dc *DeviceController) GetDevice(c *gin.Context) {
	deviceIDStr := c.Param("id")
	deviceID, err := uuid.Parse(deviceIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid device ID"})
		return
	}

	device, err := dc.deviceService.GetDevice(c.Request.Context(), deviceID)
	if err != nil {
		if err == ErrDeviceNotFound {
			c.JSON(http.StatusNotFound, gin.H{"error": "Device not found"})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get device"})
		}
		return
	}

	c.JSON(http.StatusOK, device)
}

// UpdateDevice handles device updates
func (dc *DeviceController) UpdateDevice(c *gin.Context) {
	deviceIDStr := c.Param("id")
	deviceID, err := uuid.Parse(deviceIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid device ID"})
		return
	}

	var req UpdateDeviceRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	device, err := dc.deviceService.UpdateDevice(c.Request.Context(), deviceID, req)
	if err != nil {
		if err == ErrDeviceNotFound {
			c.JSON(http.StatusNotFound, gin.H{"error": "Device not found"})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update device"})
		}
		return
	}

	c.JSON(http.StatusOK, device)
}

// DeleteDevice handles device deletion
func (dc *DeviceController) DeleteDevice(c *gin.Context) {
	deviceIDStr := c.Param("id")
	deviceID, err := uuid.Parse(deviceIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid device ID"})
		return
	}

	if err := dc.deviceService.DeleteDevice(c.Request.Context(), deviceID); err != nil {
		if err == ErrDeviceNotFound {
			c.JSON(http.StatusNotFound, gin.H{"error": "Device not found"})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete device"})
		}
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "Device deleted successfully"})
}

// ListDevices handles device listing
func (dc *DeviceController) ListDevices(c *gin.Context) {
	userID, exists := auth.GetCurrentUser(c)
	if !exists {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
		return
	}

	var req DeviceListRequest
	if err := c.ShouldBindQuery(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Set defaults
	if req.Page < 1 {
		req.Page = 1
	}
	if req.PageSize < 1 || req.PageSize > 100 {
		req.PageSize = 20
	}

	response, err := dc.deviceService.ListDevices(c.Request.Context(), req, userID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list devices"})
		return
	}

	c.JSON(http.StatusOK, response)
}

// GetDeviceShadow handles device shadow retrieval
func (dc *DeviceController) GetDeviceShadow(c *gin.Context) {
	deviceIDStr := c.Param("id")
	deviceID, err := uuid.Parse(deviceIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid device ID"})
		return
	}

	shadow, err := dc.deviceService.GetDeviceShadow(c.Request.Context(), deviceID)
	if err != nil {
		if err == ErrDeviceNotFound {
			c.JSON(http.StatusNotFound, gin.H{"error": "Device not found"})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get device shadow"})
		}
		return
	}

	c.JSON(http.StatusOK, shadow)
}

// UpdateDeviceShadow handles device shadow updates
func (dc *DeviceController) UpdateDeviceShadow(c *gin.Context) {
	deviceIDStr := c.Param("id")
	deviceID, err := uuid.Parse(deviceIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid device ID"})
		return
	}

	var req struct {
		Desired  map[string]interface{} `json:"desired"`
		Reported map[string]interface{} `json:"reported"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	shadow, err := dc.deviceService.UpdateDeviceShadow(c.Request.Context(), deviceID, req.Desired, req.Reported)
	if err != nil {
		if err == ErrDeviceNotFound {
			c.JSON(http.StatusNotFound, gin.H{"error": "Device not found"})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update device shadow"})
		}
		return
	}

	c.JSON(http.StatusOK, shadow)
}

// GetDeviceCredentials handles device credentials retrieval
func (dc *DeviceController) GetDeviceCredentials(c *gin.Context) {
	deviceIDStr := c.Param("id")
	deviceID, err := uuid.Parse(deviceIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid device ID"})
		return
	}

	credentials, err := dc.deviceService.GetDeviceCredentials(c.Request.Context(), deviceID)
	if err != nil {
		if err == ErrDeviceNotFound {
			c.JSON(http.StatusNotFound, gin.H{"error": "Device not found"})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get device credentials"})
		}
		return
	}

	c.JSON(http.StatusOK, credentials)
}

// RegenerateDeviceSecret handles device secret regeneration
func (dc *DeviceController) RegenerateDeviceSecret(c *gin.Context) {
	deviceIDStr := c.Param("id")
	deviceID, err := uuid.Parse(deviceIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid device ID"})
		return
	}

	device, err := dc.deviceService.RegenerateDeviceSecret(c.Request.Context(), deviceID)
	if err != nil {
		if err == ErrDeviceNotFound {
			c.JSON(http.StatusNotFound, gin.H{"error": "Device not found"})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to regenerate device secret"})
		}
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "Device secret regenerated successfully",
		"device":  device,
	})
}

// SetDeviceStatus handles device status updates
func (dc *DeviceController) SetDeviceStatus(c *gin.Context) {
	deviceIDStr := c.Param("id")
	deviceID, err := uuid.Parse(deviceIDStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid device ID"})
		return
	}

	status := c.Param("status")
	switch status {
	case "online":
		err = dc.deviceService.SetDeviceOnline(c.Request.Context(), deviceID)
	case "offline":
		err = dc.deviceService.SetDeviceOffline(c.Request.Context(), deviceID)
	default:
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid status. Use 'online' or 'offline'"})
		return
	}

	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update device status"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "Device status updated successfully"})
}

// GetDeviceStatistics handles device statistics retrieval
func (dc *DeviceController) GetDeviceStatistics(c *gin.Context) {
	userID, exists := auth.GetCurrentUser(c)
	if !exists {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
		return
	}

	// This would typically query the database for user-specific statistics
	// For now, we'll return mock data for the authenticated user
	stats := map[string]interface{}{
		"user_id":          userID,
		"total_devices":    0,
		"active_devices":   0,
		"offline_devices":  0,
		"inactive_devices": 0,
	}

	c.JSON(http.StatusOK, stats)
}
